package cn.edu.cuit.elena.nati;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;

import cn.edu.cuit.elena.common.SackConstants;
import cn.edu.cuit.elena.trace.Tracer;

/**
 * <code>CommandExcutor</code> is based on <code>java.lang.UNIXProcess</code> and implements
 * <code>java.lang.Runnable</code>, it run a thread to start a process to execute external command
 * string, and the thread would return after the process exited.
 * */
public class CommandExecutor
    implements Runnable
{
    private String command;
    private Process unixProcess;
    private boolean success;
    private String[] envs;
    private File workPath;

    /**
     * Construct a CommandExecutor, use specified work directory and environment variants.
     * 
     * @param command
     *            The platform dependent command.
     * @param workPath
     *            The specified work directory
     * @param envs
     *            Environment variants , as <code>key=value</code> format.
     * 
     * */
    public CommandExecutor(String command, File workPath, String... envs)
    {
        this.command = command;
        this.envs = envs;
        this.workPath = workPath;
    }

    void execute() throws IOException, ClassNotFoundException, SecurityException, NoSuchFieldException,
        IllegalArgumentException, IllegalAccessException, ErrorExitCodeException
    {
        Runtime runtime = Runtime.getRuntime();
        unixProcess = runtime.exec( command, envs, workPath );
        //        Class<? extends Process> clazz = (Class<? extends Process>) Class
        //            .forName( "java.lang.UNIXProcess" );
        //        Field field = clazz.getDeclaredField( "hasExited" );
        //        field.setAccessible( true );
        //        boolean hasExited = false;
        //        while( !hasExited )
        //        {
        //            hasExited = (Boolean) field.get( unixProcess );
        //        }

        InputStream error = unixProcess.getErrorStream();
        OutputStream out = unixProcess.getOutputStream();
        InputStream in = unixProcess.getInputStream();
        BufferedReader reader = new BufferedReader( new InputStreamReader( in ) );
        //        final BufferedReader errReader = new BufferedReader( new InputStreamReader( unixProcess.getErrorStream() ) );
        //        BufferedReader inReader = new BufferedReader( new InputStreamReader( unixProcess.getInputStream() ) );
        //        final StringBuffer errMsg = new StringBuffer();

        // read error and input streams as this would free up the buffers
        // free the error stream buffer
        //        Thread errThread = new Thread()
        //        {
        //            @Override
        //            public void run()
        //            {
        //                try
        //                {
        //                    String line = errReader.readLine();
        //                    while( ( line != null ) && !isInterrupted() )
        //                    {
        //                        errMsg.append( line );
        //                        errMsg.append( System.getProperty( "line.separator" ) );
        //                        line = errReader.readLine();
        //                    }
        //                }
        //                catch( IOException ioe )
        //                {
        //                    Tracer.debugTrace( CommandExecutor.class, "Error reading the error stream", ioe );
        //                }
        //            }
        //        };
        //
        //        try
        //        {
        //            errThread.start();
        //        }
        //        catch( IllegalStateException e )
        //        {
        //            Tracer.debugTrace( CommandExecutor.class, "Error Stream Reader thread exception : ", e );
        //        }

        String c;
        while( ( c = reader.readLine() ) != null )
        {
                        System.out.println( c );
        }
        int exitValue = -1;
        try
        {
            exitValue = unixProcess.waitFor();
            Tracer.debugTrace( getClass(), "Process exit code : " + exitValue, null );
        }
        catch( InterruptedException e )
        {
            Tracer.debugTrace( getClass(), "CommandExecutor has been interrupted", e );
            return;
        }
        //
        //        checkExit:
        //        while( exitValue != 0 )
        //        {
        //            try
        //            {
        //                exitValue = unixProcess.exitValue();
        //            }
        //            catch( IllegalThreadStateException e )
        //            {
        //                continue checkExit;
        //            }
        //        }
        //
        //        if( exitValue != 0 )
        //        {
        //            throw new ErrorExitCodeException( "Error Exit code : " + exitValue );
        //        }
    }

    @Override
    public void run()
    {
        try
        {
            execute();
            Tracer.debugTrace( getClass(), "Native command : \"" + command + "\" completed ", null );
        }
        catch( Exception e )
        {
            Tracer.fatalTrace( getClass(), "Native command executing thread occurs Exception.", e );
            success = false;
        }
        success = true;
    }

    /**
     * Each thread is a new thread object, and there is no shared data, so there is no need to
     * synchronize <code>unixProcess</code>, <code>success</code>.
     * */
    public boolean isSuccess()
    {
        return success;
    }
}
